////////////////////////////////////////////////
// DESCRIPTION:
//    Micro Framework Emulator Button
//
// Legal Notices:
//   Copyright (c) 2008, Telliam Consulting, LLC.
//   All rights reserved.
//
//   Redistribution and use in source and binary forms, with or without modification,
//   are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice, this list
//     of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice, this
//     list of conditions and the following disclaimer in the documentation and/or other
//     materials provided with the distribution.
//   * Neither the name of Telliam Consulting nor the names of its contributors may be
//     used to endorse or promote products derived from this software without specific
//     prior written permission.
//
//   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//   SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
//   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
//   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//   DAMAGE. 
//
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using Microsoft.SPOT.Emulator;
using Microsoft.SPOT.Emulator.Gpio;
using System.Runtime.CompilerServices;

namespace FusionWare.SPOT.EmulatorComponents
{
    /// <summary>Class for implementing an emulator button connected to a GPIO pin</summary>
    public class GpioButton : ImageButton, IEmulatorComponentControl
    {
        private delegate void PortWriteDelegate(bool fState);
        private PortWriteDelegate WriteDelegate;

        /// <summary>Default Constructor</summary>
        public GpioButton()
        {
            this._Port = new GpioPort();
            this._Port.ModesExpected = GpioPortMode.InputPort | GpioPortMode.OutputPort;
            this._Port.ModesAllowed = this._Port.ModesExpected;
            this.WriteDelegate = new PortWriteDelegate(this._Port.Write);
            this._Port.OnGpioActivity += new GpioActivity(_Port_OnGpioActivity);
        }

        void _Port_OnGpioActivity(GpioPort sender, bool edge)
        {
            System.Diagnostics.Debug.Print("Activity: ( {0}, {1} )", sender.Pin.ToString(), edge.ToString());
        }

        /// <summary>Port pin associated with this button</summary>
        [Browsable(false)]
        public GpioPort Port
        {
            get { return _Port;  }
        }
        private GpioPort _Port;

        /// <summary>Resistor Mode for the GPIO pin</summary>
        [
            CategoryAttribute("Emulation"),
            DescriptionAttribute("Sets the  Pin Name for the GPIO Port of this button. (CustomEmulator.GetPinByName() is used to resolve the name to a pin number.)"),
            DefaultValue(null)
        ]
        public string PinName
        {
            get { return this._PinName; }
            set { this._PinName = value; }
        }
        string _PinName;

        /// <summary>Register the GpioPort with the emulator</summary>
        /// <param name="Emulator">Emulator to register the port with</param>
        /// <remarks>
        /// This method resolves the pin name to a pin number by calling the
        /// Emulator.GetPinByName() and then registers the port with the emulator.
        /// Deferring the name resolution to the Emulator allows a great deal of
        /// flexibiliity while simplifying the use of the emulator system as a whole.
        /// </remarks>
        public void RegisterComponents(CustomEmulator Emulator)
        {
            if( Emulator == null )
                throw new ArgumentNullException( "Emulator" );

            this._Port.ComponentId = this._PinName;
            this._Port.Pin = Emulator.GetPinByName(this.PinName);
            Emulator.RegisterComponent(this._Port);
            WritePinFromButtonState(false);
        }

        /// <summary>Handles changes in the button state</summary>
        /// <param name="NewState">Indicates if the button is pressed or not</param>
        /// <remarks>
        /// This method is called by the ImageButton base class whenever the state
        /// of the button changes. This implementation will set the state of the
        /// GPIO pin and notify the emultor of the change. 
        /// 
        /// This class takes into account the pullup/pull down settings for the
        /// pin to accurately reflect the state of the pin to the emulation.
        /// </remarks>
        protected override void OnButtonStateChanged(bool NewState)
        {
            base.OnButtonStateChanged( NewState );
            if (this._Port == null)
                return;

            WritePinFromButtonState( NewState );
        }

        private void WritePinFromButtonState(bool Pressed)
        {
            bool newPinState = false;
            switch(this._Port.Resistor)
            {
            case GpioResistorMode.Disabled:
            case GpioResistorMode.PullUp:
                newPinState = !Pressed;
                break;

            case GpioResistorMode.PullDown:
                newPinState = Pressed;
                break;
            }
#if DEBUG
            System.Diagnostics.Debug.Print("Setting Pin {0} to {1}", this._Port.Pin, newPinState);
#endif
            if(this._Port.InvokeRequired)
                this._Port.Invoke(this.WriteDelegate, new object[] { newPinState });
            else
                this._Port.Write(newPinState);
        }
    }
}